home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / inn1.000 / inn1.4sec-linux-src.tar / inn / frontends / inews.c < prev    next >
C/C++ Source or Header  |  1993-03-18  |  35KB  |  1,420 lines

  1. /*  $Revision: 1.33 $
  2. **
  3. **  Send an article (prepared by someone on the local site) to the
  4. **  master news server.
  5. */
  6. #include "configdata.h"
  7. #include <stdio.h>
  8. #include <ctype.h>
  9. #include <errno.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <pwd.h>
  13. #include <grp.h>
  14. #if    defined(DO_NEED_TIME)
  15. #include <time.h>
  16. #endif    /* defined(DO_NEED_TIME) */
  17. #include <sys/time.h>
  18. #include <fcntl.h>
  19. #include "nntp.h"
  20. #include "paths.h"
  21. #include "libinn.h"
  22. #include "clibrary.h"
  23. #include "macros.h"
  24.  
  25.  
  26. #define FLUSH_ERROR(F)        (fflush((F)) == EOF || ferror((F)))
  27. #define LPAREN            '('    /* For vi :-) */
  28. #define HEADER_DELTA        20
  29. #define GECOSTERM(c)        \
  30.         ((c) == ',' || (c) == ';' || (c) == ':' || (c) == LPAREN)
  31.  
  32.  
  33. typedef enum _HEADERTYPE {
  34.     HTobs,
  35.     HTreq,
  36.     HTstd
  37. } HEADERTYPE;
  38.  
  39. typedef struct _HEADER {
  40.     STRING    Name;
  41.     BOOL    CanSet;
  42.     HEADERTYPE    Type;
  43.     int        Size;
  44.     char    *Value;
  45. } HEADER;
  46.  
  47.  
  48. STATIC BOOL    Dump;
  49. STATIC BOOL    Revoked;
  50. STATIC BOOL    Spooling;
  51. STATIC char    SPOOLNEWS[] = _PATH_SPOOLNEWS;
  52. STATIC char    **OtherHeaders;
  53. STATIC char    NGSEPS[] = NG_SEPARATOR;
  54. STATIC char    SIGSEP[] = SIG_SEPARATOR;
  55. STATIC FILE    *FromServer;
  56. STATIC FILE    *ToServer;
  57. STATIC int    OtherCount;
  58. STATIC int    OtherSize;
  59. STATIC char    *Exclusions = "";
  60. STATIC STRING    BadDistribs[] = {
  61.     BAD_DISTRIBS
  62. };
  63.  
  64. STATIC HEADER    Table[] = {
  65.     /*     Name            Canset    Type    */
  66.     {    "Path",            TRUE,    HTstd },
  67. #define _path         0
  68.     {    "From",            TRUE,    HTstd },
  69. #define _from         1
  70.     {    "Newsgroups",        TRUE,    HTreq },
  71. #define _newsgroups     2
  72.     {    "Subject",        TRUE,    HTreq },
  73. #define _subject     3
  74.     {    "Control",        TRUE,    HTstd },
  75. #define _control     4
  76.     {    "Supersedes",        TRUE,    HTstd },
  77. #define _supersedes     5
  78.     {    "Followup-To",        TRUE,    HTstd },
  79. #define _followupto     6
  80.     {    "Date",            TRUE,    HTstd },
  81. #define _date         7
  82.     {    "Organization",        TRUE,    HTstd },
  83. #define _organization     8
  84.     {    "Lines",        TRUE,    HTstd },
  85. #define _lines         9
  86.     {    "Sender",        TRUE,    HTstd },
  87. #define _sender        10
  88.     {    "Approved",        TRUE,    HTstd },
  89. #define _approved    11
  90.     {    "Distribution",        TRUE,    HTstd },
  91. #define _distribution    12
  92.     {    "Expires",        TRUE,    HTstd },
  93. #define _expires    13
  94.     {    "Message-ID",        TRUE,    HTstd },
  95. #define _messageid    14
  96.     {    "References",        TRUE,    HTstd },
  97. #define _references    15
  98.     {    "Reply-To",        TRUE,    HTstd },
  99. #define _replyto    16
  100.     {    "Also-Control",        TRUE,    HTstd },
  101. #define _alsocontrol    17
  102.     {    "Xref",            FALSE,    HTstd },
  103.     {    "Summary",        TRUE,    HTstd },
  104.     {    "Keywords",        TRUE,    HTstd },
  105.     {    "Date-Received",    FALSE,    HTobs },
  106.     {    "Received",        FALSE,    HTobs },
  107.     {    "Posted",        FALSE,    HTobs },
  108.     {    "Posting-Version",    FALSE,    HTobs },
  109.     {    "Relay-Version",    FALSE,    HTobs },
  110. };
  111.  
  112. #define HDR(_x)    (Table[(_x)].Value)
  113.  
  114.  
  115.  
  116. /*
  117. **  Send the server a quit message, wait for a reply.
  118. */
  119. STATIC NORETURN
  120. QuitServer(x)
  121.     int        x;
  122. {
  123.     char    buff[NNTP_STRLEN + 2];
  124.     char    *p;
  125.  
  126.     if (Spooling)
  127.     exit(x);
  128.     if (x)
  129.     (void)fprintf(stderr, "(Article not posted.)\n");
  130.     (void)fprintf(ToServer, "quit\r\n");
  131.     if (FLUSH_ERROR(ToServer)) {
  132.     (void)fprintf(stderr, "Can't send quit to server, %s",
  133.         strerror(errno));
  134.     exit(1);
  135.     }
  136.     if (fgets(buff, sizeof buff, FromServer) == NULL) {
  137.     (void)fprintf(stderr, "Warning -- server did not reply to quit, %s",
  138.         strerror(errno));
  139.     exit(1);
  140.     }
  141.     if ((p = strchr(buff, '\r')) != NULL)
  142.     *p = '\0';
  143.     if ((p = strchr(buff, '\n')) != NULL)
  144.     *p = '\0';
  145.     if (atoi(buff) != NNTP_GOODBYE_ACK_VAL) {
  146.     (void)fprintf(stderr, "Server didn't reply to quit properly:\n\t%s\n",
  147.         buff);
  148.     exit(1);
  149.     }
  150.     (void)fclose(FromServer);
  151.     (void)fclose(ToServer);
  152.     exit(x);
  153. }
  154.  
  155.  
  156. /*
  157. **  Print and error message (with errno) and exit with an error code.
  158. */
  159. STATIC NORETURN
  160. PerrorExit(ShouldQuit, s)
  161.     BOOL    ShouldQuit;
  162.     char    *s;
  163. {
  164.     (void)fprintf(stderr, "%s, %s.\n", s, strerror(errno));
  165.     if (ShouldQuit)
  166.     QuitServer(1);
  167.     exit(1);
  168. }
  169.  
  170.  
  171.  
  172. /*
  173. **  Flush a stdio FILE; exit if there are any errors.
  174. */
  175. STATIC void
  176. SafeFlush(F)
  177.     FILE    *F;
  178. {
  179.     if (FLUSH_ERROR(F))
  180.     PerrorExit(TRUE, "Can't send text to server");
  181. }
  182.  
  183.  
  184. /*
  185. **  Trim trailing spaces, return pointer to first non-space char.
  186. */
  187. STATIC char *
  188. TrimSpaces(p)
  189.     register char    *p;
  190. {
  191.     register char    *start;
  192.  
  193.     for (start = p; ISWHITE(*start); start++)
  194.     continue;
  195.     for (p = start + strlen(start); p > start && CTYPE(isspace, p[-1]); )
  196.     *--p = '\0';
  197.     return start;
  198. }
  199.  
  200.  
  201. /*
  202. **  Mark the end of the header starting at p, and return a pointer
  203. **  to the start of the next one.  Handles continuations.
  204. */
  205. STATIC char *
  206. NextHeader(p)
  207.     register char    *p;
  208. {
  209.     for ( ; ; p++) {
  210.     if ((p = strchr(p, '\n')) == NULL) {
  211.         (void)fprintf(stderr, "Article is all headers.\n");
  212.         QuitServer(1);
  213.     }
  214.     if (!ISWHITE(p[1])) {
  215.         *p = '\0';
  216.         return p + 1;
  217.     }
  218.     }
  219. }
  220.  
  221.  
  222. /*
  223. **  Strip any headers off the article and dump them into the table.
  224. */
  225. STATIC char *
  226. StripOffHeaders(article)
  227.     char        *article;
  228. {
  229.     register char    *p;
  230.     register char    *q;
  231.     register HEADER    *hp;
  232.     register char    c;
  233.     register int    i;
  234.  
  235.     /* Set up the other headers list. */
  236.     OtherSize = HEADER_DELTA;
  237.     OtherHeaders = NEW(char*, OtherSize);
  238.     OtherCount = 0;
  239.  
  240.     /* Scan through buffer, a header at a time. */
  241.     for (i = 0, p = article; ; i++) {
  242.  
  243.     if ((q = strchr(p, ':')) == NULL) {
  244.         (void)fprintf(stderr, "No colon in header line \"%.20s...\"\n",
  245.             p);
  246.         QuitServer(1);
  247.     }
  248.     if (q[1] == '\n' && !ISWHITE(q[2])) {
  249.         /* Empty header; ignore this one, get next line. */
  250.         p = NextHeader(p);
  251.         if (*p == '\n')
  252.         break;
  253.     }
  254.  
  255.     if (q[1] != '\0' && !ISWHITE(q[1])) {
  256.         if ((q = strchr(q, '\n')) != NULL)
  257.         *q = '\0';
  258.         (void)fprintf(stderr, "No space after colon in \"%.20s...\"\n", p);
  259.         QuitServer(1);
  260.     }
  261.  
  262.     /* See if it's a known header. */
  263.     c = CTYPE(islower, *p) ? toupper(*p) : *p;
  264.     for (hp = Table; hp < ENDOF(Table); hp++)
  265.         if (c == hp->Name[0]
  266.          && p[hp->Size] == ':'
  267.          && ISWHITE(p[hp->Size + 1])
  268.          && caseEQn(p, hp->Name, hp->Size)) {
  269.         if (hp->Type == HTobs) {
  270.             (void)fprintf(stderr, "Obsolete \"%s\" header.\n",
  271.                 hp->Name);
  272.             QuitServer(1);
  273.         }
  274.         if (hp->Value) {
  275.             (void)fprintf(stderr, "Duplicate \"%s\" header.\n",
  276.                 hp->Name);
  277.             QuitServer(1);
  278.         }
  279.         for (q = &p[hp->Size + 1]; ISWHITE(*q); q++)
  280.             continue;
  281.         hp->Value = q;
  282.         break;
  283.         }
  284.  
  285.     /* Too many headers? */
  286.     if (++i > 5 * HEADER_DELTA) {
  287.         (void)fprintf(stderr, "More than %d lines of header.\n", i);
  288.         QuitServer(1);
  289.     }
  290.  
  291.     /* No; add it to the set of other headers. */
  292.     if (hp == ENDOF(Table)) {
  293.         if (OtherCount >= OtherSize - 1) {
  294.         OtherSize += HEADER_DELTA;
  295.         RENEW(OtherHeaders, char*, OtherSize);
  296.         }
  297.         OtherHeaders[OtherCount++] = p;
  298.     }
  299.  
  300.     /* Get start of next header; if it's a blank line, we hit the end. */
  301.     p = NextHeader(p);
  302.     if (*p == '\n')
  303.         break;
  304.     }
  305.  
  306.     return p + 1;
  307. }
  308.  
  309.  
  310.  
  311. /*
  312. **  See if the user is allowed to cancel the indicated message.  Assumes
  313. **  that the Sender or From line has already been filled in.
  314. */
  315. STATIC void
  316. CheckCancel(msgid, JustReturn)
  317.     char        *msgid;
  318.     BOOL        JustReturn;
  319. {
  320.     char        localfrom[SMBUF];
  321.     register char    *p;
  322.     char        buff[BUFSIZ];
  323.     char        remotefrom[SMBUF];
  324.  
  325.     /* Ask the server for the article. */
  326.     (void)fprintf(ToServer, "head %s\r\n", msgid);
  327.     SafeFlush(ToServer);
  328.     if (fgets(buff, sizeof buff, FromServer) == NULL
  329.      || atoi(buff) != NNTP_HEAD_FOLLOWS_VAL) {
  330.     if (JustReturn)
  331.         return;
  332.     (void)fprintf(stderr, "Server has no such article.\n");
  333.     QuitServer(1);
  334.     }
  335.  
  336.     /* Read the headers, looking for the From or Sender. */
  337.     remotefrom[0] = '\0';
  338.     while (fgets(buff, sizeof buff, FromServer) != NULL) {
  339.     if ((p = strchr(buff, '\r')) != NULL)
  340.         *p = '\0';
  341.     if ((p = strchr(buff, '\n')) != NULL)
  342.         *p = '\0';
  343.     if (buff[0] == '.' && buff[1] == '\0')
  344.         break;
  345.     if (EQn(buff, "Sender:", 7))
  346.         (void)strcpy(remotefrom, TrimSpaces(&buff[7]));
  347.     else if (remotefrom[0] == '\0' && EQn(buff, "From:", 5))
  348.         (void)strcpy(remotefrom, TrimSpaces(&buff[5]));
  349.     }
  350.     if (remotefrom[0] == '\0') {
  351.     if (JustReturn)
  352.         return;
  353.     (void)fprintf(stderr, "Article is garbled.\n");
  354.     QuitServer(1);
  355.     }
  356.     HeaderCleanFrom(remotefrom);
  357.  
  358.     /* Get the local user. */
  359.     (void)strcpy(localfrom, HDR(_sender) ? HDR(_sender) : HDR(_from));
  360.     HeaderCleanFrom(localfrom);
  361.  
  362.     /* Is the right person cancelling? */
  363.     if (!EQ(localfrom, remotefrom)) {
  364.     (void)fprintf(stderr,
  365.         "Article was posted by \"%s\" and you are \"%s\".\n",
  366.         remotefrom, localfrom);
  367.     QuitServer(1);
  368.     }
  369. }
  370.  
  371.  
  372. /*
  373. **  See if the user is the news administrator.
  374. */
  375. STATIC BOOL
  376. AnAdministrator(name, group)
  377.     char        *name;
  378.     int            group;
  379. {
  380.     struct passwd    *pwp;
  381.     struct group    *grp;
  382.     char        **mem;
  383.     char        *p;
  384.  
  385.     if (Revoked)
  386.     return FALSE;
  387.  
  388.     /* Find out who we are. */
  389.     if ((pwp = getpwnam(NEWSUSER)) == NULL)
  390.     /* Silent falure; clients might not have the group. */
  391.     return FALSE;
  392.     if (getuid() == pwp->pw_uid)
  393.     return TRUE;
  394.  
  395.     /* See if the we're in the right group. */
  396.     if ((grp = getgrnam(NEWSGID)) == NULL || (mem = grp->gr_mem) == NULL)
  397.     /* Silent falure; clients might not have the group. */
  398.     return FALSE;
  399.     if (group == grp->gr_gid)
  400.     return TRUE;
  401.     while ((p = *mem++) != NULL)
  402.     if (EQ(name, p))
  403.         return TRUE;
  404.     return FALSE;
  405. }
  406.  
  407.  
  408. /*
  409. **  Check the control message, and see if it's legit.
  410. */
  411. STATIC void
  412. CheckControl(ctrl, pwp)
  413.     char        *ctrl;
  414.     struct passwd    *pwp;
  415. {
  416.     register char    *p;
  417.     register char    *q;
  418.     char        save;
  419.     char        name[SMBUF];
  420.  
  421.     /* Snip off the first word. */
  422.     for (p = ctrl; ISWHITE(*p); p++)
  423.     continue;
  424.     for (ctrl = p; *p && !ISWHITE(*p); p++)
  425.     continue;
  426.     if (p == ctrl) {
  427.     (void)fprintf(stderr, "Empty control message.\n");
  428.     QuitServer(1);
  429.     }
  430.     save = *p;
  431.     *p = '\0';
  432.  
  433.     if (EQ(ctrl, "cancel")) {
  434.     for (q = p + 1; ISWHITE(*q); q++)
  435.         continue;
  436.     if (*q == '\0') {
  437.         (void)fprintf(stderr, "Message-ID missing in cancel.\n");
  438.         QuitServer(1);
  439.     }
  440.     if (!Spooling)
  441.         CheckCancel(q, FALSE);
  442.     }
  443.     else if (EQ(ctrl, "checkgroups")
  444.       || EQ(ctrl, "ihave")
  445.       || EQ(ctrl, "sendme")
  446.       || EQ(ctrl, "newgroup")
  447.       || EQ(ctrl, "rmgroup")
  448.       || EQ(ctrl, "sendsys")
  449.       || EQ(ctrl, "senduuname")
  450.       || EQ(ctrl, "version")) {
  451.     (void)strcpy(name, pwp->pw_name);
  452.     if (!AnAdministrator(name, (int)pwp->pw_gid)) {
  453.         (void)fprintf(stderr,
  454.             "Ask your news administrator to do the \"%s\" for you.\n",
  455.             ctrl);
  456.         QuitServer(1);
  457.     }
  458.     }
  459.     else {
  460.     (void)fprintf(stderr, "\"%s\" is not a valid control message.\n",
  461.         ctrl);
  462.     QuitServer(1);
  463.     }
  464.     *p = save;
  465. }
  466.  
  467.  
  468.  
  469. /*
  470. **  Parse the GECOS field to get the user's full name.  This comes Sendmail's
  471. **  buildfname routine.  Ignore leading stuff like "23-" "stuff]-" or
  472. **  "stuff -" as well as trailing whitespace, or anything that comes after
  473. **  a comma, semicolon, or in parentheses.  This seems to strip off most of
  474. **  the UCB or ATT stuff people fill out the entries with.  Also, turn &
  475. **  into the login name, with perhaps an initial capital.  (Everyone seems
  476. **  to hate that, but everyone also supports it.)
  477. */
  478. STATIC char *
  479. FormatUserName(pwp, node)
  480.     struct passwd    *pwp;
  481.     char        *node;
  482. {
  483.     char    buff[BUFSIZ];
  484.     char    outbuff[SMBUF];
  485.     char    *out;
  486.     char    *p;
  487.  
  488. #if    defined(DONT_MUNGE_GECOS)
  489.     (void)strcpy(outbuff, pwp->pw_gecos);
  490. #else
  491.     p = pwp->pw_gecos;
  492.     if (*p == '*')
  493.     p++;
  494.     for (out = outbuff; *p && !GECOSTERM(*p); p++) {
  495.     if (*p == '&') {
  496.         (void)strcpy(out, pwp->pw_name);
  497.         if (CTYPE(islower, *out)
  498.          && (out == outbuff || !isalpha(out[-1])))
  499.         *out = toupper(*out);
  500.         while (*out)
  501.         out++;
  502.     }
  503.     else if (*p == '-'
  504.           && p > pwp->pw_gecos
  505.           && (isdigit(p[-1]) || isspace(p[-1]) || p[-1] == ']'))
  506.         out = outbuff;
  507.     else
  508.         *out++ = *p;
  509.     }
  510.     *out = '\0';
  511. #endif    /* defined(DONT_MINGE_GECOS) */
  512.  
  513.     out = TrimSpaces(outbuff);
  514.     if (out[0])
  515.     (void)sprintf(buff, "%s@%s (%s)", pwp->pw_name, node, out);
  516.     else
  517.     (void)sprintf(buff, "%s@%s", pwp->pw_name, node);
  518.     return COPY(buff);
  519. }
  520.  
  521.  
  522. /*
  523. **  Check the Distribution header, and exit on error.
  524. */
  525. STATIC void
  526. CheckDistribution(p)
  527.     register char    *p;
  528. {
  529.     static char        SEPS[] = " \t,";
  530.     register STRING    *dp;
  531.  
  532.     if ((p = strtok(p, SEPS)) == NULL) {
  533.     (void)fprintf(stderr, "Can't parse Distribution line.\n");
  534.     QuitServer(1);
  535.     }
  536.     do {
  537.     for (dp = BadDistribs; *dp; dp++)
  538.         if (wildmat(p, *dp)) {
  539.         (void)fprintf(stderr, "Illegal distribution \"%s\"\n", p);
  540.         QuitServer(1);
  541.         }
  542.     } while ((p = strtok((char *)NULL, SEPS)) != NULL);
  543. }
  544.  
  545.  
  546. /*
  547. **  Process all the headers.  FYI, they're done in RFC-order.
  548. */
  549. STATIC void
  550. ProcessHeaders(AddOrg, linecount, pwp)
  551.     BOOL        AddOrg;
  552.     int            linecount;
  553.     struct passwd    *pwp;
  554. {
  555.     static char        MONTHS[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
  556.     static char        PATHFLUFF[] = PATHMASTER;
  557.     static char        SIGNS[] = "+-";
  558.     register HEADER    *hp;
  559.     register char    *p;
  560.     TIMEINFO        Now;
  561.     struct tm        *tm;
  562.     char        buff[SMBUF];
  563.     char        from[SMBUF];
  564.     int            i;
  565.     long        zone;
  566.  
  567.     /* Do some preliminary fix-ups. */
  568.     for (hp = Table; hp < ENDOF(Table); hp++) {
  569.     if (!hp->CanSet && hp->Value) {
  570.         (void)fprintf(stderr, "Can't set system \"%s\" header.\n",
  571.             hp->Name);
  572.         QuitServer(1);
  573.     }
  574.     if (hp->Value) {
  575.         hp->Value = TrimSpaces(hp->Value);
  576.         if (*hp->Value == '\0')
  577.         hp->Value = NULL;
  578.     }
  579.     }
  580.  
  581.     /* Set From or Sender. */
  582.     if ((p = GetConfigValue(_CONF_FROMHOST)) == NULL)
  583.     PerrorExit(TRUE, "Can't get host name");
  584.     if (HDR(_from) == NULL)
  585.     HDR(_from) = FormatUserName(pwp, p);
  586.     else {
  587.     (void)sprintf(buff, "%s@%s", pwp->pw_name, p);
  588.     (void)strcpy(from, HDR(_from));
  589.     HeaderCleanFrom(from);
  590.     if (!EQ(from, buff))
  591.         HDR(_sender) = COPY(buff);
  592.     }
  593.  
  594.     /* Set Date. */
  595.     if (GetTimeInfo(&Now) < 0)
  596.     PerrorExit(TRUE, "Can't get the time");
  597.     if ((tm = localtime(&Now.time)) == NULL)
  598.     PerrorExit(TRUE, "Can't convert to local time");
  599.  
  600.     /* The %0n.nd contruct from <kre@munnari.oz.au> is clever.  Modern
  601.      * printf's treat it %02 (two digits wide) .2 (zero-fill to at least
  602.      * two digits), while old versions treat it as %02 (zero-fill two
  603.      * digits wide) .2 (noise).  You might want to check this on your
  604.      * system. */
  605.     if (Now.tzone < 0) {
  606.     p = &SIGNS[0];
  607.     zone = -Now.tzone;
  608.     }
  609.     else {
  610.     p = &SIGNS[1];
  611.     zone = Now.tzone;
  612.     }
  613.     (void)sprintf(buff, "%d %3.3s %d %02.2d:%02.2d:%02.2d %c%04.4d",
  614.     tm->tm_mday, &MONTHS[3 * tm->tm_mon], 1900 + tm->tm_year,
  615.     tm->tm_hour, tm->tm_min, tm->tm_sec,
  616.     *p, (int)((zone / 60) * 100 + (zone % 60)));
  617.     HDR(_date) = COPY(buff);
  618.  
  619.     /* Newsgroups are checked later. */
  620.  
  621.     /* Set Subject; Control overrides the subject. */
  622.     if (HDR(_control)) {
  623.     CheckControl(HDR(_control), pwp);
  624.     HDR(_subject) = NEW(char, 5 + strlen(HDR(_control)) + 1);
  625.     (void)sprintf(HDR(_subject), "cmsg %s", HDR(_control));
  626.     }
  627.     else {
  628.     p = HDR(_subject);
  629.     if (p == NULL) {
  630.         (void)fprintf(stderr,
  631.             "Required \"Subject\" header is missing or empty.\n");
  632.         QuitServer(1);
  633.     }
  634.     if (EQn(p, "cmsg ", 5)) {
  635.         HDR(_control) = p + 5;
  636.         CheckControl(HDR(_control), pwp);
  637.     }
  638.     else if (HDR(_alsocontrol))
  639.         CheckControl(HDR(_alsocontrol), pwp);
  640. #if    0
  641.     if (EQn(p, "Re: ", 4) && HDR(_references) == NULL) {
  642.         (void)fprintf(stderr,
  643.             "Article starts with \"Re: \" but has no references.\n");
  644.         QuitServer(1);
  645.     }
  646. #endif    /* 0 */
  647.     }
  648.  
  649.     /* Set Message-ID */
  650.     if (HDR(_messageid) == NULL) {
  651.     if ((p = GenerateMessageID()) == NULL) {
  652.         (void)fprintf(stderr, "Can't generate Message-ID, %s\n",
  653.             strerror(errno));
  654.         QuitServer(1);
  655.     }
  656.     HDR(_messageid) = COPY(p);
  657.     }
  658.     else if ((p = strchr(HDR(_messageid), '@')) == NULL
  659.        || strchr(++p, '@') != NULL) {
  660.     (void)fprintf(stderr, "Message-ID must have exactly one '@'\n");
  661.     QuitServer(1);
  662.     }
  663.  
  664.     /* Set Path */
  665.     if (HDR(_path) == NULL) {
  666.     i = strlen(Exclusions) + STRLEN(PATHFLUFF);
  667. #if    defined(DO_INEWS_PATH)
  668.     if ((p = GetFileConfigValue(_CONF_PATHHOST)) != NULL) {
  669.         i += strlen(p) + 1;
  670.         HDR(_path) = NEW(char, i + 1);
  671.         if (*p)
  672.         (void)sprintf(HDR(_path), "%s%s!%s", Exclusions, p, PATHFLUFF);
  673.         else
  674.         (void)sprintf(HDR(_path), "%s%s", Exclusions, PATHFLUFF);
  675.     }
  676.     else if (GetFileConfigValue(_CONF_SERVER) != NULL) {
  677.         if ((p = GetFQDN()) == NULL) {
  678.         (void)fprintf(stderr, "Can't get host name, %s\n",
  679.             strerror(errno));
  680.         QuitServer(1);
  681.         }
  682.         i += strlen(p) + 1;
  683.         HDR(_path) = NEW(char, i + 1);
  684.         (void)sprintf(HDR(_path), "%s%s!%s", Exclusions, p, PATHFLUFF);
  685.     }
  686.     else {
  687.         HDR(_path) = NEW(char, i + 1);
  688.         (void)sprintf(HDR(_path), "%s%s", Exclusions, PATHFLUFF);
  689.     }
  690. #else
  691.     HDR(_path) = NEW(char, i + 1);
  692.     (void)sprintf(HDR(_path), "%s%s", Exclusions, PATHFLUFF);
  693. #endif    /* defined(DO_INEWS_PATH) */
  694.     }
  695.  
  696.     /* Reply-To; left alone. */
  697.     /* Sender; set above. */
  698.     /* Followup-To; checked with Newsgroups. */
  699.  
  700.     /* Check Expires. */
  701.     if (HDR(_expires) && parsedate(HDR(_expires), &Now) == -1) {
  702.     (void)fprintf(stderr, "Can't parse \"%s\" as an expiration date.\n",
  703.         HDR(_expires));
  704.     QuitServer(1);
  705.     }
  706.  
  707.     /* References; left alone. */
  708.     /* Control; checked above. */
  709.  
  710.     /* Distribution. */
  711.     if ((p = HDR(_distribution)) != NULL) {
  712.     p = COPY(p);
  713.     CheckDistribution(p);
  714.     DISPOSE(p);
  715.     }
  716.  
  717.     /* Set Organization. */
  718.     if (AddOrg
  719.      && HDR(_organization) == NULL
  720.      && (p = GetConfigValue(_CONF_ORGANIZATION)) != NULL) {
  721.     HDR(_organization) = COPY(p);
  722.     }
  723.  
  724.     /* Keywords; left alone. */
  725.     /* Summary; left alone. */
  726.     /* Approved; left alone. */
  727.  
  728.     /* Set Lines */
  729.     (void)sprintf(buff, "%d", linecount);
  730.     HDR(_lines) = COPY(buff);
  731.  
  732.     /* Check Supersedes. */
  733.     if (HDR(_supersedes))
  734.     CheckCancel(HDR(_supersedes), TRUE);
  735.  
  736.     /* Now make sure everything is there. */
  737.     for (hp = Table; hp < ENDOF(Table); hp++)
  738.     if (hp->Type == HTreq && hp->Value == NULL) {
  739.         (void)fprintf(stderr,
  740.             "Required \"%s\" header is missing or empty.\n",
  741.             hp->Name);
  742.         QuitServer(1);
  743.     }
  744. }
  745.  
  746.  
  747. /*
  748. **  Try to append $HOME/.signature to the article.  When in doubt, exit
  749. **  out in order to avoid postings like "Sorry, I forgot my .signature
  750. **  -- here's the article again."
  751. */
  752. STATIC char *
  753. AppendSignature(UseMalloc, article, homedir, linesp)
  754.     BOOL    UseMalloc;
  755.     char    *article;
  756.     char    *homedir;
  757.     int        *linesp;
  758. {
  759.     static char    NOSIG[] = "Can't add your .signature (%s), article not posted";
  760.     int        i;
  761.     int        length;
  762.     char    *p;
  763.     char    buff[BUFSIZ];
  764.     FILE    *F;
  765.  
  766.     /* Open the file. */
  767.     *linesp = 0;
  768.     (void)sprintf(buff, "%s/.signature", homedir);
  769.     if ((F = fopen(buff, "r")) == NULL) {
  770.     if (errno == ENOENT)
  771.         return article;
  772.     (void)fprintf(stderr, NOSIG, strerror(errno));
  773.     QuitServer(1);
  774.     }
  775.  
  776.     /* Read it in. */
  777.     length = fread((POINTER)buff, (SIZE_T)1, (SIZE_T)sizeof buff - 2, F);
  778.     i = feof(F);
  779.     (void)fclose(F);
  780.     if (length == 0) {
  781.     (void)fprintf(stderr, NOSIG, "empty file");
  782.     QuitServer(1);
  783.     }
  784.     if (length < 0) {
  785.     (void)fprintf(stderr, NOSIG, strerror(errno));
  786.     QuitServer(1);
  787.     }
  788.     if (length == sizeof buff - 2 && !i) {
  789.     (void)fprintf(stderr, NOSIG, "too big");
  790.     QuitServer(1);
  791.     }
  792.  
  793.     /* Make sure the buffer ends with \n\0. */
  794.     if (buff[length - 1] != '\n')
  795.     buff[length++] = '\n';
  796.     buff[length] = '\0';
  797.  
  798.     /* Count the lines. */
  799.     for (i = 0, p = buff; (p = strchr(p, '\n')) != NULL; p++)
  800.     if (++i > SIG_MAXLINES) {
  801.         (void)fprintf(stderr, NOSIG, "too many lines");
  802.         QuitServer(1);
  803.     }
  804.     *linesp = 1 + i;
  805.  
  806.     /* Grow the article to have the signature. */
  807.     i = strlen(article);
  808.     if (UseMalloc) {
  809.     p = NEW(char, i + (sizeof SIGSEP - 1) + length + 1);
  810.     (void)strcpy(p, article);
  811.     article = p;
  812.     }
  813.     else
  814.     RENEW(article, char, i + (sizeof SIGSEP - 1) + length + 1);
  815.     (void)strcpy(&article[i], SIGSEP);
  816.     (void)strcpy(&article[i + sizeof SIGSEP - 1], buff);
  817.     return article;
  818. }
  819.  
  820.  
  821. #if    defined(DO_CHECK_INCLUDED_TEXT)
  822. /*
  823. **  See if the user has more included text than new text.  Simple-minded, but
  824. **  reasonably effective for catching neophyte's mistakes.  A line starting
  825. **  with > is included text.  Decrement the count on lines starting with <
  826. **  so that we don't reject diff(1) output.
  827. */
  828. STATIC void
  829. CheckIncludedText(p, lines)
  830.     register char    *p;
  831.     register int    lines;
  832. {
  833.     register int    i;
  834.  
  835.     for (i = 0; ; p++) {
  836.     switch (*p) {
  837.     case '>':
  838.         i++;
  839.         break;
  840.     case '<':
  841.         i--;
  842.         break;
  843.     }
  844.     if ((p = strchr(p + 1, '\n')) == NULL)
  845.         break;
  846.     }
  847.     if (i * 2 > lines) {
  848.     (void)fprintf(stderr,
  849.         "Article not posted -- more included text than new text\n");
  850.     QuitServer(1);
  851.     }
  852. }
  853. #endif    /* defined(DO_CHECK_INCLUDED_TEXT) */
  854.  
  855.  
  856.  
  857. /*
  858. **  Try to mail an article to the moderator of the group.
  859. */
  860. STATIC BOOL
  861. MailArticle(group, article)
  862.     char        *group;
  863.     char        *article;
  864. {
  865.     register FILE    *F;
  866.     register HEADER    *hp;
  867.     register int    i;
  868.     char        *address;
  869.     char        buff[SMBUF];
  870.  
  871.     /* Try to get the address first. */
  872.     if ((address = GetModeratorAddress(group)) == NULL) {
  873.     (void)fprintf(stderr,
  874.         "The \"%s\" newsgroup is moderated, but has no address;\n",
  875.         group);
  876.     (void)fprintf(stderr, "ask your news administrator to fix this.\n");
  877.     return FALSE;
  878.     }
  879.  
  880.     /* Say what we're going to do. */
  881.     (void)printf(
  882.     "The \"%s\" newsgroup is moderated.  Your article will not be\n",
  883.     group);
  884.     (void)printf("posted, but mailed to the moderator for approval.\n");
  885.  
  886.     /* Now build up the command (ignore format/argument mismatch errors,
  887.      * in case %s isn't in _PATH_SENDMAIL) and send the headers. */
  888.     (void)sprintf(buff, _PATH_SENDMAIL, address);
  889.     if ((F = popen(buff, "w")) == NULL)
  890.     PerrorExit(TRUE, "Can't start mailer");
  891.     (void)fprintf(F, "To: %s\n", address);
  892.     SafeFlush(F);
  893.  
  894.     /* Write the headers, a blank line, then the article. */
  895.     for (hp = Table; hp < ENDOF(Table); hp++)
  896.     if (hp->Value) {
  897.         (void)fprintf(F, "%s: %s\n", hp->Name, hp->Value);
  898.         SafeFlush(F);
  899.     }
  900.     for (i = 0; i < OtherCount; i++) {
  901.     (void)fprintf(F, "%s\n", OtherHeaders[i]);
  902.     SafeFlush(F);
  903.     }
  904.     (void)fprintf(F, "\n");
  905.     i = strlen(article);
  906.     if (fwrite((POINTER)article, (SIZE_T)1, (SIZE_T)i, F) != i)
  907.     PerrorExit(TRUE, "Can't send article");
  908.     SafeFlush(F);
  909.     i = pclose(F);
  910.     if (i) {
  911.     (void)fprintf(stderr, "Mailer exited with status %d;\n", i);
  912.     (void)fprintf(stderr, "Article might not have been mailed.\n");
  913.     return FALSE;
  914.     }
  915.     return TRUE;
  916. }
  917.  
  918.  
  919. /*
  920. **  Check the newsgroups, make sure they're all valid, that none are
  921. **  moderated, etc.
  922. */
  923. STATIC BOOL
  924. ValidNewsgroups(hdr, F, article)
  925.     char        *hdr;
  926.     FILE        *F;
  927.     char        *article;
  928. {
  929.     register char    *groups;
  930.     register char    *p;
  931.     register int    i;
  932.     BOOL        approved;
  933.     BOOL        mailed;
  934.     BOOL        FoundOne;
  935.     char        *group;
  936.     char        *q;
  937.     char        buff[SMBUF];
  938.     struct _DDHANDLE    *h;
  939.     BOOL        IsNewgroup;
  940.  
  941.     p = HDR(_control);
  942.     IsNewgroup = p && EQn(p, "newgroup", 8);
  943.  
  944.     groups = COPY(hdr);
  945.     if ((p = strtok(groups, NGSEPS)) == NULL) {
  946.     (void)fprintf(stderr, "Can't parse newsgroups line.\n");
  947.     return FALSE;
  948.     }
  949.  
  950.     /* Don't mail article if just checking Followup-To line. */
  951.     approved = HDR(_approved) != NULL || article == NULL;
  952.     FoundOne = FALSE;
  953.  
  954.     h = DDstart(FromServer, ToServer);
  955.     do {
  956. #if    defined(DO_MERGE_TO_GROUPS)
  957.     if (p[0] == 't' && p[1] == 'o' && p[2] == '.')
  958.         p = "to";
  959. #endif    /* defined(DO_MERGE_TO_GROUPS) */
  960.     i = strlen(p);
  961.     (void)fseek(F, (OFFSET_T)0, SEEK_SET);
  962.     while (fgets(buff, sizeof buff, F) != NULL)
  963.         if (buff[0] == *p && EQn(buff, p, i) && buff[i] == ' ')
  964.         break;
  965.     if (feof(F))
  966.         continue;
  967.     FoundOne = TRUE;
  968.     DDcheck(h, p);
  969.  
  970.     /* Skip past the newsgroup name, the high and low counts, to find
  971.      * the flags. */
  972.     for (group = p, p = &buff[i]; *p == ' ' || CTYPE(isdigit, *p); p++)
  973.         continue;
  974.  
  975.     switch (*p) {
  976.     case NF_FLAG_OK:
  977.         break;
  978.     case NF_FLAG_MODERATED:
  979.         if (Dump)
  980.         (void)fprintf(stderr,
  981.             "%s is moderated -- article would be mailed\n",
  982.             group);
  983.         else if (!approved) {
  984.         mailed = MailArticle(group, article);
  985.         DISPOSE(DDend(h));
  986.         QuitServer(mailed ? 0 : 1);
  987.         }
  988.         break;
  989.     case NF_FLAG_IGNORE:
  990.     case NF_FLAG_NOLOCAL:
  991.         (void)fprintf(stderr, "Postings to \"%s\" are not allowed here.\n",
  992.             group);
  993.         DISPOSE(DDend(h));
  994.         return FALSE;
  995.     case NF_FLAG_EXCLUDED:
  996.         (void)fprintf(stderr, "Warning:  \"%s\" is rejected here.\n",
  997.             group);
  998.         /* Do NOT return false. */
  999.         break;
  1000.     case NF_FLAG_ALIAS:
  1001.         if ((q = strchr(p, '\n')) != NULL)
  1002.         *q = '\0';
  1003.         (void)fprintf(stderr,
  1004.             "The newsgroup \"%s\" has been renamed to \"%s\".\n",
  1005.             group, p + 1);
  1006.         DISPOSE(DDend(h));
  1007.         return FALSE;
  1008.     }
  1009.  
  1010.     } while ((p = strtok((char *)NULL, NGSEPS)) != NULL);
  1011.  
  1012.     if (!FoundOne && !IsNewgroup) {
  1013.     (void)fprintf(stderr, "No such newsgroups as \"%s\".\n", hdr);
  1014.     DISPOSE(DDend(h));
  1015.     return FALSE;
  1016.     }
  1017.  
  1018.     /* Set default distribution. */
  1019.     p = DDend(h);
  1020.     if (HDR(_distribution) == NULL && *p)
  1021.     HDR(_distribution) = p;
  1022.  
  1023.     return TRUE;
  1024. }
  1025.  
  1026.  
  1027.  
  1028. /*
  1029. **  Read stdin into a string and return it.  Can't use ReadInDescriptor
  1030. **  since that will fail if stdin is a tty.
  1031. */
  1032. STATIC char *
  1033. ReadStdin()
  1034. {
  1035.     register int    size;
  1036.     register char    *p;
  1037.     char        *article;
  1038.     register char    *end;
  1039.     register int    i;
  1040.  
  1041.     size = BUFSIZ;
  1042.     article = NEW(char, size);
  1043.     end = &article[size - 3];
  1044.     for (p = article; (i = getchar()) != EOF; *p++ = (char)i)
  1045.     if (p == end) {
  1046.         RENEW(article, char, size + BUFSIZ);
  1047.         p = &article[size - 3];
  1048.         size += BUFSIZ;
  1049.         end = &article[size - 3];
  1050.     }
  1051.  
  1052.     /* Force a \n terminator. */
  1053.     if (p > article && p[-1] != '\n')
  1054.     *p++ = '\n';
  1055.     *p = '\0';
  1056.     return article;
  1057. }
  1058.  
  1059.  
  1060.  
  1061. /*
  1062. **  Offer the article to the server, return its reply.
  1063. */
  1064. STATIC int
  1065. OfferArticle(buff, Authorized)
  1066.     char        *buff;
  1067.     BOOL        Authorized;
  1068. {
  1069.     (void)fprintf(ToServer, "post\r\n");
  1070.     SafeFlush(ToServer);
  1071.     if (fgets(buff, NNTP_STRLEN, FromServer) == NULL)
  1072.     PerrorExit(TRUE,
  1073.         Authorized ? "Can't offer article to server (authorized)"
  1074.                : "Can't offer article to server");
  1075.     return atoi(buff);
  1076. }
  1077.  
  1078.  
  1079. /*
  1080. **  Make a temporary filename that is unlikely to collide with mktemp
  1081. **  or cause problems for sites with 14-character filename limits.
  1082. */
  1083. STATIC void
  1084. TempName(dir, buff)
  1085.     char    *dir;
  1086.     char    *buff;
  1087. {
  1088.     time_t    now;
  1089.  
  1090.     (void)time(&now);
  1091.     now &= 0xFFFFFFFF;
  1092.     (void)sprintf(buff, "%s/%08.8lxXXXXXX", dir, (long)now);
  1093.     (void)mktemp(buff);
  1094. }
  1095.  
  1096.  
  1097. /*
  1098. **  Spool article to temp file.
  1099. */
  1100. STATIC void
  1101. Spoolit(article, Length, deadfile)
  1102.     char        *article;
  1103.     SIZE_T        Length;
  1104.     char        *deadfile;
  1105. {
  1106.     static char        SPOOLTEMP[] = _PATH_SPOOLTEMP;
  1107.     register HEADER    *hp;
  1108.     register FILE    *F;
  1109.     register int    i;
  1110.     char        temp[BUFSIZ];
  1111.     char        buff[BUFSIZ];
  1112.     struct stat        Sb;
  1113.  
  1114.     /* Try to write to the spool dir, else the deadfile. */
  1115.     if ((stat(SPOOLNEWS, &Sb) >= 0 && S_ISDIR(Sb.st_mode))
  1116.      || (F = xfopena(deadfile)) == NULL) {
  1117.     TempName(SPOOLTEMP, temp);
  1118.     (void)umask(0);
  1119.     if ((i = open(temp, O_WRONLY | O_CREAT, BATCHFILE_MODE)) < 0
  1120.      || (F = fdopen(i, "w")) == NULL)
  1121.         PerrorExit(FALSE, "Can't create spool file");
  1122.     deadfile = NULL;
  1123.     }
  1124.  
  1125.     /* Write the headers and a blank line. */
  1126.     for (hp = Table; hp < ENDOF(Table); hp++)
  1127.     if (hp->Value)
  1128.         (void)fprintf(F, "%s: %s\n", hp->Name, hp->Value);
  1129.     for (i = 0; i < OtherCount; i++)
  1130.     (void)fprintf(F, "%s\n", OtherHeaders[i]);
  1131.     (void)fprintf(F, "\n");
  1132.     if (FLUSH_ERROR(stdout))
  1133.     PerrorExit(FALSE, "Can't write headers");
  1134.  
  1135.     /* Write the article and exit. */
  1136.     if (fwrite((POINTER)article, (SIZE_T)1, Length, F) != Length)
  1137.     PerrorExit(FALSE, "Can't write article");
  1138.     SafeFlush(F);
  1139.     if (fclose(F) == EOF)
  1140.     PerrorExit(FALSE, "Can't close spool file");
  1141.  
  1142.     if (deadfile == NULL) {
  1143.     /* Put the file in a good place. */
  1144.     TempName(SPOOLNEWS, buff);
  1145.     if (rename(temp, buff) < 0)
  1146.         PerrorExit(FALSE, "Can't rename spool file");
  1147.     }
  1148. }
  1149.  
  1150.  
  1151. /*
  1152. **  Print usage message and exit.
  1153. */
  1154. STATIC NORETURN
  1155. Usage()
  1156. {
  1157.     (void)fprintf(stderr, "Usage: inews [-D] [-h] [header_flags] [article]\n");
  1158.     /* Don't call QuitServer here -- connection isn't open yet. */
  1159.     exit(1);
  1160. }
  1161.  
  1162.  
  1163. int
  1164. main(ac, av)
  1165.     int            ac;
  1166.     char        *av[];
  1167. {
  1168.     static char        NOCONNECT[] = "Can't connect to server";
  1169.     register int    i;
  1170.     register char    *p;
  1171.     register HEADER    *hp;
  1172.     int            j;
  1173.     int            Mode;
  1174.     int            SigLines;
  1175.     FILE        *F;
  1176.     struct passwd    *pwp;
  1177.     char        *article;
  1178.     char        *deadfile;
  1179.     char        buff[NNTP_STRLEN + 2];
  1180.     char        SpoolMessage[NNTP_STRLEN + 2];
  1181.     BOOL        DoSignature;
  1182.     BOOL        AddOrg;
  1183.     SIZE_T        Length;
  1184.  
  1185.     /* Find out who we are. */
  1186.     if ((i = geteuid()) < 0)
  1187.     PerrorExit(TRUE, "Can't get your user ID");
  1188.     if ((pwp = getpwuid((UID_T)i)) == NULL)
  1189.     PerrorExit(TRUE, "Can't get your password entry");
  1190.  
  1191.     /* Set defaults. */
  1192.     Mode = '\0';
  1193.     Dump = FALSE;
  1194.     DoSignature = TRUE;
  1195.     AddOrg = TRUE;
  1196.     (void)umask(NEWSUMASK);
  1197.  
  1198.     /* Parse JCL. */
  1199.     while ((i = getopt(ac, av, "DNAVWORShx:a:c:d:e:f:n:r:t:F:o:w:")) != EOF)
  1200.     switch (i) {
  1201.     default:
  1202.         Usage();
  1203.         /* NOTREACHED */
  1204.     case 'D':
  1205.     case 'N':
  1206.         Dump = TRUE;
  1207.         break;
  1208.     case 'A':
  1209.     case 'V':
  1210.     case 'W':
  1211.         /* Ignore C News options. */
  1212.         break;
  1213.     case 'O':
  1214.         AddOrg = FALSE;
  1215.         break;
  1216.     case 'R':
  1217.         Revoked = TRUE;
  1218.         break;
  1219.     case 'S':
  1220.         DoSignature = FALSE;
  1221.         break;
  1222.     case 'h':
  1223.         Mode = i;
  1224.         break;
  1225.     case 'x':
  1226.         Exclusions = NEW(char, strlen(optarg) + 1 + 1);
  1227.         (void)sprintf(Exclusions, "%s!", optarg);
  1228.         break;
  1229.     /* Header lines that can be specified on the command line. */
  1230.     case 'a':    HDR(_approved) = optarg;        break;
  1231.     case 'c':    HDR(_control) = optarg;            break;
  1232.     case 'd':    HDR(_distribution) = optarg;        break;
  1233.     case 'e':    HDR(_expires) = optarg;            break;
  1234.     case 'f':    HDR(_from) = optarg;            break;
  1235.     case 'n':    HDR(_newsgroups) = optarg;        break;
  1236.     case 'r':    HDR(_replyto) = optarg;            break;
  1237.     case 't':    HDR(_subject) = optarg;            break;
  1238.     case 'F':    HDR(_references) = optarg;        break;
  1239.     case 'o':    HDR(_organization) = optarg;        break;
  1240.     case 'w':    HDR(_followupto) = optarg;        break;
  1241.     }
  1242.     ac -= optind;
  1243.     av += optind;
  1244.  
  1245.     /* Parse positional arguments; at most one, the input file. */
  1246.     switch (ac) {
  1247.     default:
  1248.     Usage();
  1249.     /* NOTREACHED */
  1250.     case 0:
  1251.     /* Read stdin. */
  1252.     article = ReadStdin();
  1253.     break;
  1254.     case 1:
  1255.     /* Read named file. */
  1256.     article = ReadInFile(av[0], (struct stat *)NULL);
  1257.     if (article == NULL)
  1258.         PerrorExit(FALSE, "Can't read input file");
  1259.     break;
  1260.     }
  1261.  
  1262.     /* Try to open a connection to the server. */
  1263.     if (NNTPremoteopen(&FromServer, &ToServer, buff) < 0) {
  1264.     Spooling = TRUE;
  1265.     if ((p = strchr(buff, '\n')) != NULL)
  1266.         *p = '\0';
  1267.     if ((p = strchr(buff, '\r')) != NULL)
  1268.         *p = '\0';
  1269.     (void)strcpy(SpoolMessage, buff[0] ? buff : NOCONNECT);
  1270.     (void)sprintf(buff, "%s/dead.article", pwp->pw_dir);
  1271.     deadfile = COPY(buff);
  1272.     }
  1273.     else {
  1274.     /* See if we can post. */
  1275.     i = atoi(buff);
  1276.  
  1277.     /* Tell the server we're posting. */
  1278.     setbuf(FromServer, NEW(char, BUFSIZ));
  1279.     setbuf(ToServer, NEW(char, BUFSIZ));
  1280.     (void)fprintf(ToServer, "mode reader\r\n");
  1281.     SafeFlush(ToServer);
  1282.     if (fgets(buff, NNTP_STRLEN, FromServer) == NULL)
  1283.         PerrorExit(TRUE, "Can't tell server we're reading");
  1284.     if ((j = atoi(buff)) != NNTP_BAD_COMMAND_VAL)
  1285.         i = j;
  1286.  
  1287.     if (i != NNTP_POSTOK_VAL) {
  1288.         (void)fprintf(stderr, "You do not have permission to post.\n");
  1289.         QuitServer(1);
  1290.         exit(1);
  1291.     }
  1292.     }
  1293.  
  1294.     /* Basic processing. */
  1295.     for (hp = Table; hp < ENDOF(Table); hp++)
  1296.     hp->Size = strlen(hp->Name);
  1297.     if (Mode == 'h')
  1298.     article = StripOffHeaders(article);
  1299.     for (i = 0, p = article; (p = strchr(p, '\n')) != NULL; i++, p++)
  1300.     continue;
  1301. #if    defined(DO_CHECK_INCLUDED_TEXT)
  1302.     CheckIncludedText(article, i);
  1303. #endif    /* defined(DO_CHECK_INCLUDED_TEXT) */
  1304.     if (DoSignature)
  1305.     article = AppendSignature(Mode == 'h', article, pwp->pw_dir, &SigLines);
  1306.     else
  1307.     SigLines = 0;
  1308.     ProcessHeaders(AddOrg, i + SigLines, pwp);
  1309.     Length = strlen(article);
  1310. #if    LOCAL_MAX_ARTSIZE > 0
  1311.     if (Length > LOCAL_MAX_ARTSIZE) {
  1312.     (void)fprintf(stderr,
  1313.         "Article is bigger then local limit of %ld bytes\n",
  1314.         LOCAL_MAX_ARTSIZE);
  1315.     QuitServer(1);
  1316.     }
  1317. #endif    /* LOCAL_MAX_ARTSIZE > 0 */
  1318.  
  1319.     /* Do final checks. */
  1320.     if (i == 0 && HDR(_control) == NULL) {
  1321.     (void)fprintf(stderr, "Article is empty.\n");
  1322.     QuitServer(1);
  1323.     }
  1324.     for (hp = Table; hp < ENDOF(Table); hp++)
  1325.     if (hp->Value && (int)strlen(hp->Value) + hp->Size > NNTP_STRLEN) {
  1326.         (void)fprintf(stderr, "\"%s\" header is too long.\n", hp->Name);
  1327.         QuitServer(1);
  1328.     }
  1329.     for (i = 0; i < OtherCount; i++)
  1330.     if ((int)strlen(OtherHeaders[i]) > NNTP_STRLEN) {
  1331.         (void)fprintf(stderr,
  1332.             "Header too long (%d characters max):\n\t%40.40s...\n",
  1333.             NNTP_STRLEN, OtherHeaders[i]);
  1334.         QuitServer(1);
  1335.     }
  1336.  
  1337.     /* Check the newsgroups. */
  1338.     if ((F = CAlistopen(FromServer, ToServer, (char *)NULL)) != NULL) {
  1339.     if (!ValidNewsgroups(HDR(_newsgroups), F, article)) {
  1340.         CAclose();
  1341.         QuitServer(1);
  1342.     }
  1343.     if ((p = HDR(_followupto)) != NULL
  1344.      && !EQ(p, "poster")
  1345.      && !ValidNewsgroups(p, F, (char *)NULL)) {
  1346.         CAclose();
  1347.         QuitServer(1);
  1348.     }
  1349.     CAclose();
  1350.     }
  1351.     else if (!Spooling)
  1352.     PerrorExit(TRUE, "Can't get list of newsgroups");
  1353.  
  1354.     if (Dump) {
  1355.     /* Write the headers and a blank line. */
  1356.     for (hp = Table; hp < ENDOF(Table); hp++)
  1357.         if (hp->Value)
  1358.         (void)printf("%s: %s\n", hp->Name, hp->Value);
  1359.     for (i = 0; i < OtherCount; i++)
  1360.         (void)printf("%s\n", OtherHeaders[i]);
  1361.     (void)printf("\n");
  1362.     if (FLUSH_ERROR(stdout))
  1363.         PerrorExit(TRUE, "Can't write headers");
  1364.  
  1365.     /* Write the article and exit. */
  1366.     if (fwrite((POINTER)article, (SIZE_T)1, Length, stdout) != Length)
  1367.         PerrorExit(TRUE, "Can't write article");
  1368.     SafeFlush(stdout);
  1369.     QuitServer(0);
  1370.     }
  1371.  
  1372.     if (Spooling) {
  1373.     (void)fprintf(stderr, "Warning %s -- Article will be spooled.\n",
  1374.         SpoolMessage);
  1375.     Spoolit(article, Length, deadfile);
  1376.     exit(0);
  1377.     }
  1378.  
  1379.     /* Article is prepared, offer it to the server. */
  1380.     i = OfferArticle(buff, FALSE);
  1381.     if (i == NNTP_AUTH_NEEDED_VAL) {
  1382.     /* Posting not allowed, try to authorize. */
  1383.     if (NNTPsendpassword((char *)NULL, FromServer, ToServer) < 0)
  1384.         PerrorExit(TRUE, "Authorization error");
  1385.     i = OfferArticle(buff, TRUE);
  1386.     }
  1387.     if (i != NNTP_START_POST_VAL) {
  1388.     (void)fprintf(stderr, "Server doesn't want the article:\n\t%s\n",
  1389.         buff);
  1390.     QuitServer(1);
  1391.     }
  1392.  
  1393.     /* Write the headers, a blank line, then the article. */
  1394.     for (hp = Table; hp < ENDOF(Table); hp++)
  1395.     if (hp->Value)
  1396.         (void)fprintf(ToServer, "%s: %s\r\n", hp->Name, hp->Value);
  1397.     for (i = 0; i < OtherCount; i++)
  1398.     (void)fprintf(ToServer, "%s\r\n", OtherHeaders[i]);
  1399.     (void)fprintf(ToServer, "\r\n");
  1400.     if (NNTPsendarticle(article, ToServer, TRUE) < 0)
  1401.     PerrorExit(TRUE, "Can't send article to server");
  1402.     SafeFlush(ToServer);
  1403.  
  1404.     if (fgets(buff, sizeof buff, FromServer) == NULL)
  1405.     PerrorExit(TRUE, "No reply from server after sending the article");
  1406.     if ((p = strchr(buff, '\r')) != NULL)
  1407.     *p = '\0';
  1408.     if ((p = strchr(buff, '\n')) != NULL)
  1409.     *p = '\0';
  1410.     if (atoi(buff) != NNTP_POSTEDOK_VAL) {
  1411.     (void)fprintf(stderr, "Can't send article to the server:\n\t%s\n",
  1412.         buff);
  1413.     QuitServer(1);
  1414.     }
  1415.  
  1416.     /* Close up. */
  1417.     QuitServer(0);
  1418.     /* NOTREACHED */
  1419. }
  1420.